// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define  STATUS_STRING_OWNER
#define  ERROR_STRING_OWNER

#include <sstream>
#include <boost/format.hpp>
#include "Session.hpp"
#include "FunctionInterface.hpp"
#include "Global.hpp"
#include "CallException.hpp"
#include "Arg.hpp"
#include "Submit.hpp"
#include "FpOutputStream.hpp"

using namespace ninf::client;
int
Session :: counter = 10;

/// constructor
/// Todo: syncFlag seems not working
Session :: Session(const char * entry, bool syncFlag)
  : syncFlag(syncFlag), args(NULL), status(NC_SESSION_INITIAL),
    errorCode(NINF_NO_ERROR), submit(NULL)
{
  id = counter++;
  interface = Global::global.interfaceMap[entry];
  if (interface == NULL)
    throw CALL_EXCEPTION("no such interface", NINF_NO_STUB_INFO);

  Global::global.sessionManager.registerSession(this);
}

/// destructor
Session :: ~Session(){
  if (submit != NULL)
    delete submit;
  if (args != NULL)
    delete args;

  for(unsigned i = 0; i < toFree.size(); i++)
	  free((char *)toFree[i]);
}


//
void
Session :: setupMeta(){
  for (int i = 0; i < interface->getNParams(); i++)
     args[i].setupGdata();
}


// write arguments to the file
void
Session :: writeOutMeta(
    const string & outputFilePrefix,
    const string & inputFilePrefix,
    const string & metaFilePrefix) {
  for (int i = 0; i < interface->getNParams(); i++) {
    std::string outfile = (boost::format("%1%.%2%") % outputFilePrefix % i).str();
    std::string infile = (boost::format("%1%.%2%") % inputFilePrefix % i).str();
    ninf::data::GrpcData modData = args[i].gdata->rfileMod(outfile, infile);
    std::string metafile =(boost::format("%1%.%2%") % metaFilePrefix % i).str();
    modData.writeTo(metafile.c_str());
    submit->addStageIn(strdup(metafile.c_str()));
  }
}

// write arguments to the file
void
Session :: writeOut(const string & outputFilePrefix) {
   for (int i = 0; i < interface->getNParams(); i++)
				if ((args[i].modeType == MODE_IN    ||
						 args[i].modeType == MODE_INOUT ||
						 args[i].dataType == DT_FILENAME) &&
             args[i].toBeWriteOut()){
          std::string outfile = (boost::format("%1%.%2%") % outputFilePrefix % i).str();
          FpOutputStream   fos(outfile);
          DataOutputStream dos(&fos);
          args[i].writeData(dos);
          submit->addStageIn(strdup(outfile.c_str()));
          }
}

// read the result from the file
void
Session :: readIn(const string & inputFilePrefix){
		// readInArgs
		for (int i = 0; i < interface->getNParams(); i++)
				if ((args[i].modeType == MODE_OUT ||
						 args[i].modeType == MODE_INOUT) &&
						 args[i].nDim > 0 &&
						 args[i].toBeWriteOut()) {
            std::string infile = (boost::format("%1%.%2%") % inputFilePrefix % i).str();
          FpInputStream   fis(infile);
          DataInputStream dis(&fis);
          args[i].readData(dis);
				}
}


NinfErrorCode
Session :: invoke(va_list ap){
  args = new Arg[interface->getNParams()];
  // fill the args
  interface->setupArgs(ap, args);
  submit = new Submit(interface->getModuleName(),
			interface->getEntryName(),
			id);
  if (Global::global.universe == UNIVERSE_VANILLA ||
      Global::global.universe == UNIVERSE_GLOBUS){
    submit->replaceFilename(args, interface->getNParams(), toFree);
  }

  try {
    // setup grpcData in Args;
    setupMeta();
    // output meta info for grpcData
    writeOutMeta(
        submit->getRelativeOut(),
        submit->getRelativeIn(),
        submit->getMetaPrefix());

    // write in memory data to the file
    writeOut(submit->getOutputPrefix());
  } catch (Exception e){
    throw CALL_EXCEPTION("failed to write", NINF_COMMUNICATION_ERROR);
  }

  try {
    submit->submit();
  } catch (Exception e){
    status = NC_SESSION_DONE;
    errorCode = NINF_SUBMIT_ERROR;
    throw CALL_EXCEPTION("failed to submit", NINF_SUBMIT_ERROR);
  }
  status = NC_SESSION_SUBMITTED;
  return NINF_NO_ERROR;
}

/**
 * finalize the session, read the data in file,
 * if needed,
 * and updata grpcdata so that the all the output uris are
 * included in input uris.
 */
NinfErrorCode
Session:: recvData(){
  try {
    readIn(submit->getInputPrefix());

    for (int i = 0; i < interface->getNParams(); i++) {
      std::cout << "-->" << std::endl;
      std::cout << *(args[i].gdata) << std::endl;
      args[i].gdata->update();
      std::cout << *(args[i].gdata) << std::endl;
      std::cout << "  <--" << std::endl;
    }

  } catch (Exception e){
    status = NC_SESSION_DONE;
    errorCode = NINF_INTERNAL_ERROR;
    throw CALL_EXCEPTION("failed to read result", NINF_COMMUNICATION_ERROR);
  }
  status = NC_SESSION_DONE;
  return NINF_NO_ERROR;
}

NinfErrorCode
Session:: abort(){
  status = NC_SESSION_FAILED;
  errorCode = NINF_CANCELED;
  return NINF_NO_ERROR;
}

